在演算法題目中,很多時候會遇到計算某個區間的總和,例如:
如果用最直覺的暴力法,每次查詢都需要 O(n) 時間,但當查詢次數很多時會超時。
解決方法就是前綴和 (Prefix Sum),只要一次預處理,就能做到每次查詢 O(1)。
給定陣列 nums,定義前綴和陣列 prefix:
prefix[i] = nums[0] + nums[1] + ... + nums[i]
利用前綴和,我們可以在 O(1) 的時間計算任意區間 [L, R] 的總和:
sum(L, R) = prefix[R] - prefix[L - 1]
假設陣列:
nums = [2, 4, 5, 7]
prefix = [2, 6, 11, 18]
若要求區間 [1,3] (即 nums[1]+nums[2]+nums[3]):
sum(1,3) = prefix[3] - prefix[0] = 18 - 2 = 16
一次計算完成,效率比暴力法快非常多。
方法 | 預處理時間 | 每次查詢時間 | 適用情境 |
---|---|---|---|
暴力法 | O(1) | O(n) | 查詢少 |
前綴和 | O(n) | O(1) | 查詢多 |
原題:
https://cses.fi/problemset/task/1646
題意:
給定一個長度為 n 的陣列,處理 q 次查詢,每次查詢輸出區間 [a, b] 的總和。
解題思路:
預先計算前綴和。
對於每次查詢 [a, b],輸出:
prefix[b]−prefix[a−1]
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, q;
cin >> n >> q;
vector<long long> nums(n + 1, 0), prefix(n + 1, 0);
for (int i = 1; i <= n; i++) {
cin >> nums[i];
prefix[i] = prefix[i - 1] + nums[i];
}
while (q--) {
int a, b;
cin >> a >> b;
cout << prefix[b] - prefix[a - 1] << "\n";
}
return 0;
}
原題:
https://cses.fi/problemset/task/1662
題意:
給定一個長度為 n 的陣列,求出有多少個子陣列的總和能被 n 整除。
解題思路:
cnt = Σ C(k, 2)
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<long long> nums(n);
for (int i = 0; i < n; i++) cin >> nums[i];
long long ans = 0, sum = 0;
unordered_map<int, long long> mp;
mp[0] = 1; // 初始條件
for (int i = 0; i < n; i++) {
sum += nums[i];
int mod = ((sum % n) + n) % n; // 避免負數
ans += mp[mod];
mp[mod]++;
}
cout << ans << "\n";
return 0;
}
原題:
https://leetcode.com/problems/range-sum-query-immutable/description/
題意:
實作一個類別 NumArray,支援以下操作:
初始化:輸入整數陣列。
查詢:回傳區間 [i, j] 的總和。
class NumArray {
public:
vector<int> prefix; // 前綴和陣列
// 初始化前綴和
NumArray(vector<int>& nums) {
int n = nums.size();
prefix.resize(n + 1, 0); // 多開一格,prefix[0] = 0
for (int i = 0; i < n; i++) {
prefix[i + 1] = prefix[i] + nums[i];
}
}
// 查詢區間 [left, right] 的總和
int sumRange(int left, int right) {
return prefix[right + 1] - prefix[left];
}
};
複雜度分析:
操作 | 時間複雜度 | 空間複雜度 |
---|---|---|
初始化前綴和 | O(n) | O(n) |
單次查詢 | O(1) | O(1) |
原題:
https://leetcode.com/problems/subarray-sum-equals-k/description/
題意:
給定一個整數陣列 nums 和一個整數 k,請計算總共有多少個子陣列的總和等於 k。
解題思路
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
unordered_map<int, int> mp;
mp[0] = 1;
int sum = 0, ans = 0;
for (int x : nums) {
sum += x;
if (mp.count(sum - k))
ans += mp[sum - k];
mp[sum]++;
}
return ans;
}
};
複雜度分析:
操作 | 時間複雜度 | 空間複雜度 |
---|---|---|
遍歷整個陣列 | O(n) | O(n) |
查詢 HashMap | O(1) 平均 | O(n) |